package prod_cons;

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class OvertakersTest {
	
	private static class DummyTask implements Runnable {
		public int id;
		AtomicInteger clock;
		public int retrievalTime;
		
		public DummyTask(int id, AtomicInteger clock) {
			this.id = id;
			this.clock = clock;
		}
		public String toString() {
			return Integer.toString(id);
		}
		public void run() {
			this.retrievalTime = clock.getAndIncrement();
			Random r = new Random();
			boolean longTask = (r.nextFloat() < 0.05f);
			int shortTaskLength = 100;
			int length = longTask ? shortTaskLength * 1000 : shortTaskLength;
			
			int sum = 0; 
			for(int i = 1; i < length; i++) {
				sum += Math.cosh(Math.log(Math.sqrt(i)));
			}
		}
	}
	
	private static class Inserter implements Runnable {
		TaskContainer container;
		AtomicInteger clock = new AtomicInteger(0);
		private final int tasksNum = 1000000;
		DummyTask[] tasks = new DummyTask[tasksNum];
		private int max = 0;
		
		public int getMax() {
			return max;
		}
		
		public Inserter(TaskContainer container) {
			this.container = container;
			for(int i = 0; i < tasksNum; i++) {
				tasks[i] = new DummyTask(i, clock);
			}
		}
		
		public void run() {
			for(int i = 0; i < tasks.length; i++) {
				this.container.put(tasks[i]);
			}
		}
		
		public void calcOvertakers() {
			// pass over the array of tasks, calc the max
			for(DummyTask task : tasks) {
				int delay = task.retrievalTime - task.id;
				if (delay > max) {
					max = delay;
				}
			}
		}
	}
	
	private static class Remover implements Runnable {
		TaskContainer container;
		volatile boolean stop = false;
		
		public Remover(TaskContainer container) {
			this.container = container;
		}
		
		public void stop() {
			this.stop = true;
		}
		
		private boolean retrieveTask() {
			Runnable r = container.get();
			if (r != null) {
				r.run();
			}
			return (r != null); 
		}
		public void run() {
			container.registerConsumer();
			while(!stop) {
				retrieveTask();
			}
			while(retrieveTask());
		}
	}
	
	public static void main(String[] args) throws InterruptedException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		int insertersNum = Integer.parseInt(args[0]);
		int removersNum = Integer.parseInt(args[1]);
		String containerType = args[2];

		TaskContainer container = (TaskContainer)Class.forName(containerType).newInstance();
		container.setConsumersNum(removersNum);
		Inserter[] inserters = new Inserter[insertersNum];
		Thread[] inserterThreads = new Thread[insertersNum];
		for(int i = 0; i < inserters.length; i++) {
			inserters[i] = new Inserter(container);
			inserterThreads[i] = new Thread(inserters[i]);
		}
		
		Remover[] removers = new Remover[removersNum];
		Thread[] removerThreads = new Thread[removersNum];
		for(int i = 0; i < removers.length; i++) {
			removers[i] = new Remover(container);
			removerThreads[i] = new Thread(removers[i]);
		}
		
		// start the threads
		for(Thread t : inserterThreads) {
			t.start();
		}
		for(Thread t : removerThreads) {
			t.start();
		}

		for(Thread t : inserterThreads) {
			t.stop();
			t.join();
		}
		for(Remover remover : removers)
			remover.stop();
				
		for(Thread t : removerThreads) {
			t.stop();
			t.join();
		}
		
		// print statistics
		int maxOvertakers = 0;
		for(Inserter i : inserters) {
			i.calcOvertakers();
			if (i.getMax() > maxOvertakers) maxOvertakers = i.getMax();
		}
		System.out.println("Max num of overtakers = " + maxOvertakers);
	}
}
